/**
 * Cola cuyos elementos son trabajados bajo el patron Worker.
 * 
 * @author HPCIII_Team2013
 */

package cl.ucn.hpc.webCrawler;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;

import cl.ucn.hpc.webCrawler.IQueue;
import cl.ucn.hpc.webCrawler.IRunnableTask;
import cl.ucn.hpc.webCrawler.Spider;

public class ConcreteQueue implements IQueue {
	private LinkedBlockingQueue<IRunnableTask> tareas;
	private boolean waiting;
	private boolean shutdown;
	private List<Spider> spiders;
	PoolSpider pool;
	
	/**
	 * Constructor de la clase.
	 * 
	 * @param cantSpiders Cantidad de trabajadores que operaran sobre la lista de trabajos.
	 * @param poolRec El pool de spiders al que esta asociado.
	 */
	public ConcreteQueue(int cantSpiders, PoolSpider poolRec) {
		tareas = new LinkedBlockingQueue<IRunnableTask>(1000);
		waiting = false;
		shutdown = false;
		pool = poolRec;
		spiders = new ArrayList<Spider>();
		
		for(int i=0; i<cantSpiders; i++){
			Spider spider = new Spider(this,pool);
			spiders.add(spider);
			new Thread(spider).start();
		}
	}

	/**
	 * Cambia el estado (shutdown) de la cola.
	 * 
	 * @param isShutdown El nuevo estado de la cola.
	 */
	public void setShutdown(boolean isShutdown){
		shutdown = isShutdown;
	}

	/**
	 * Ingresa un elemento a la cola.
	 * 
	 * @param r Nueva tarea a agregar.
	 * @see cl.ucn.hpc.webCrawler.IQueue#put(cl.ucn.hpc.webCrawler.IRunnableTask)
	 */
	@Override
	public boolean put(IRunnableTask r) {		
		if (waiting) {
			synchronized (this) {
				notifyAll();
			}
		}
		
		return tareas.offer(r);
	}
	
	/**
	 * Entrega y saca de la cola el primer elemento.
	 * 
	 * @return El primer elemento de la cola.
	 * @see cl.ucn.hpc.webCrawler.IQueue#take()
	 */
	@Override
	public IRunnableTask take() throws InterruptedException {	
		if (tareas.isEmpty()) {
			synchronized (this) {
				waiting = true;

				try {
					wait();
				} catch (InterruptedException e) {
					waiting = false;
				}
			}
		}
			return tareas.take();		
	}

	/**
	 * Informa si la cola esta activa.
	 * 
	 * @return Estado de la cola: 'true' si est� activa, 'false' si no.
	 * @see cl.ucn.hpc.webCrawler.IQueue#isShutDown()
	 */
	@Override
	public boolean isShutDown() {
		return shutdown;
	}
	
	/**
	 * Puede aumentar el numero de trabajadores hasta cierto limite, pero no disminuirlo.
	 * 
	 * @param nuevaCant Numero de trabajadores que operaran sobre la cola.
	 * @see cl.ucn.hpc.webCrawler.IQueue#setCantTrabajadores(int)
	 */
	@Override
	public void setCantTrabajadores(int nuevaCant){
		if(nuevaCant > spiders.size()){
			for(int i=0; i<(nuevaCant-spiders.size()); i++){
				Spider spider = new Spider(this,pool);
				spiders.add(spider);
				new Thread(spider).start();
			}
		}
	}
}
